package merisis.impl;

import java.util.HashMap;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

import merisis.util.BitSet;
import merisis.util.BitSetIterator;
import merisis.util.OpenBitSetImpl;

import org.apache.solr.util.OpenBitSet;

import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

public class Siloyard {

	final HashMap<Long, Silo> rootMap = new HashMap<Long, Silo>();

	final private HashMap<Long, Long> surfaceMap = new HashMap<Long, Long>();

	final HashMap<Long, BitSet> portsMap = new HashMap<Long, BitSet>();

	@Inject
	private Hippocampus hippocampus = null;

	@Inject
	private Injector injector;

	@Inject
	private Logger logger;

	public long[] limits(long ts[], long s) {

		if (ts.length == 0)
			return null;

		OpenBitSet bs = new OpenBitSet();
		Silo spdl = spindle(ts[0]);

		long[] tlimits = spdl.limit(s);
		if (tlimits == null)
			return null;

		for (int j = 0; j < tlimits.length; j++) {
			bs.set(tlimits[j]);
		}

		for (int i = 1; i < ts.length; i++) {

			spdl = spindle(ts[i]);
			tlimits = spdl.limit(s);
			OpenBitSet bs1 = new OpenBitSet();
			for (int j = 0; j < tlimits.length; j++) {
				bs1.set(tlimits[j]);
			}

			bs.and(bs1);
		}

		long[] result = new long[(int) bs.cardinality()];

		int k = 0;
		for (long v = bs.nextSetBit(0); v >= 0; v = bs.nextSetBit(v + 1)) {
			result[k++] = v;
		}

		return result;
	}

	public boolean check(long t[], long s) {

		long[] r = limits(t, s);

		return r != null && r.length > 0;
	}

	public void registerPort(long root, BitSet ports) {

		BitSetIterator itr = ports.iterator();
		while (itr.hasNext()) {
			long port = itr.next();

			BitSet spindles = portsMap.get(port);
			if (spindles == null) {
				spindles = new OpenBitSetImpl();
				portsMap.put(port, spindles);
			}

			spindles.set(root);
		}
	}

	private Silo spindle(long t) {

		Silo spdl = rootMap.get(t);
		if (spdl == null) {

			spdl = new Silo(this, t);
			injector.injectMembers(spdl);

			rootMap.put(t, spdl);
		}

		return spdl;
	}

	public void dumpDebugInfo() {

		if (!logger.isLoggable(Level.FINEST))
			return;

		StringBuilder sb = new StringBuilder();
		for (Silo so : rootMap.values()) {

			sb.append("sp[");
			sb.append(so.getTopVertex());
			sb.append(";");
			sb.append(so.getPorts().toString());
			sb.append("]");
			sb.append(" 0 < ");
			int depth = 0;
			Iterator<SiloDisk> itr = so.iterator();
			while (itr.hasNext()) {
				SiloDisk spl = itr.next();
				spl.dumpLayerInfo(sb);
				sb.append(" < ");
				depth++;
			}
			sb.append("\n");
		}

		for (long port : portsMap.keySet()) {
			sb.append("port[");
			sb.append(port);
			sb.append("]: ");
			sb.append(portsMap.get(port).toString());
			sb.append("\n");
		}

		logger.finest("dump messages\n" + sb.toString());
	}
}